package com.microservice.common.aspect;

import com.microservice.api.user.UserServiceClient;
import com.microservice.api.common.Result;
import com.microservice.common.annotation.RequireRole;
import com.microservice.common.context.UserContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Set;

/**
 * 角色验证切面
 * 用于验证用户是否拥有访问接口所需的角色
 */
@Slf4j
@Aspect
@Component
@Order(1) // 在PermissionAspect之前执行
public class RoleAspect {
    
    @Autowired
    private UserServiceClient userServiceClient;
    
    public RoleAspect() {
        log.info("=== RoleAspect 角色验证切面已加载 ===");
    }
    
    /**
     * 环绕通知：拦截所有带@RequireRole注解的方法
     */
    @Around("@annotation(com.microservice.common.annotation.RequireRole) || @within(com.microservice.common.annotation.RequireRole)")
    public Object checkRole(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("=== 角色验证切面被触发 ===");
        log.info("拦截方法: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        
        // 获取当前用户ID
        Long userId = UserContext.getUserId();
        if (userId == null) {
            log.warn("角色验证失败：用户未登录");
            throw new RuntimeException("角色验证失败：用户未登录");
        }
        
        // 获取方法上的@RequireRole注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        RequireRole requireRole = method.getAnnotation(RequireRole.class);
        
        // 如果方法上没有注解，尝试从类上获取
        if (requireRole == null) {
            requireRole = method.getDeclaringClass().getAnnotation(RequireRole.class);
        }
        
        if (requireRole == null) {
            // 没有角色注解，直接放行
            return joinPoint.proceed();
        }
        
        // 获取需要的角色
        String[] requiredRoles = requireRole.value();
        if (requiredRoles == null || requiredRoles.length == 0) {
            // 没有指定具体角色，直接放行
            return joinPoint.proceed();
        }
        
        // 从用户服务获取用户的所有角色
        Result<Set<String>> rolesResult = userServiceClient.getUserRoles(userId);
        if (rolesResult == null || rolesResult.getCode() != 200 || rolesResult.getData() == null) {
            log.warn("获取用户角色失败，userId: {}", userId);
            throw new RuntimeException("角色验证失败：无法获取用户角色");
        }
        
        Set<String> userRoles = rolesResult.getData();
        log.info("用户角色: {}, 需要角色: {}", userRoles, Arrays.toString(requiredRoles));
        
        // 验证角色
        boolean hasRole = checkUserRoles(userRoles, requiredRoles, requireRole.logical());
        
        if (!hasRole) {
            String username = UserContext.getUsername();
            log.warn("用户 {} (ID:{}) 角色不足，需要角色: {}, 拥有角色: {}", 
                    username, userId, Arrays.toString(requiredRoles), userRoles);
            throw new RuntimeException("访问被拒绝：您没有访问该资源所需的角色");
        }
        
        // 角色验证通过，继续执行方法
        log.info("用户 {} (ID:{}) 角色验证通过", UserContext.getUsername(), userId);
        return joinPoint.proceed();
    }
    
    /**
     * 检查用户角色
     * @param userRoles 用户拥有的角色
     * @param requiredRoles 需要的角色
     * @param logical 角色关系（AND/OR）
     * @return 是否有角色
     */
    private boolean checkUserRoles(Set<String> userRoles, String[] requiredRoles, RequireRole.Logical logical) {
        if (logical == RequireRole.Logical.AND) {
            // AND关系：需要拥有所有角色
            for (String role : requiredRoles) {
                if (!userRoles.contains(role)) {
                    return false;
                }
            }
            return true;
        } else {
            // OR关系：拥有任一角色即可
            for (String role : requiredRoles) {
                if (userRoles.contains(role)) {
                    return true;
                }
            }
            return false;
        }
    }
}
